#ifndef __TINY_TEST__
#include "vbb_GSM.h"
#include "fw/vbb_USART.h"
#include "fw/vbb_RTC.h"
#include "vbb_FirmwareUtils.h"
#include "vbb_Storage.h"
#include "vbb_FlashMem.h"
#include "vbb_SAVE.h"
#include "vbb_GPS.h"
#include <stdio.h>

#include "vbb_GSM_CNS01.h"
#include "vbb_GSM_CNS03.h"
#include "vbb_GSM_BA01.h"
#include "vbb_GSM_Communicator.h"

char _gsm_IMEI[32];
char _gsm_Identity[32];
char _gsm_tmp[1024];

VBB_USART gsmUsart;
VBB_Message gsmMessage;
char gsmBuffer[GSM_BUFFER_SIZE];
VBB_Pin PWRKEY_Pin;
VBB_Pin STATUS_Pin;

unsigned int GSM_SignalQuality;

extern VBB_RTC rtc;
extern VBB_Storage storage;
extern VBB_FlashMem flashMem;

char _gsm_CurATCmd[256];
unsigned int _gsm_PreviousCmdTime;
bool _gsm_CurATCmdDone;

unsigned int GSM_State = GSM_BEFORE_REBOOT;

extern bool communication_inprocess;
extern unsigned int server_online;

void GSM_SwitchOff(void)
{
    if (vbb_DeviceModel() == CNS03)
        GSM_SwitchOff_CNS03();
    else if (vbb_DeviceModel() == BA01)
        GSM_SwitchOff_BA01();
}

void GSM_SwitchOn(void)
{
    if (vbb_DeviceModel() == CNS03)
        GSM_SwitchOn_CNS03();
    else if (vbb_DeviceModel() == BA01)
        GSM_SwitchOn_BA01();
}

bool GSM_IsOn(void)
{
    if (vbb_DeviceModel() == CNS01)
        return GSM_IsOn_CNS01();
    else if (vbb_DeviceModel() == CNS03)
        return GSM_IsOn_CNS03();
    else if (vbb_DeviceModel() == BA01)
        return GSM_IsOn_BA01();
    else
        return FALSE;
}

bool GSM_IsOff(void)
{
    if (vbb_DeviceModel() == CNS03)
        return GSM_IsOff_CNS03();
    else if (vbb_DeviceModel() == BA01)
        return GSM_IsOff_BA01();
    else
        return FALSE;
} 

bool GSM_Ready(void)
{
    if (vbb_DeviceModel() == CNS01)
        return GSM_Ready_CNS01();
    else if (vbb_DeviceModel() == CNS03)
        return GSM_Ready_CNS03();
    else if (vbb_DeviceModel() == BA01)
        return GSM_Ready_BA01();
    else
        return FALSE;
}

void GSM_PutCommand(const char * Command)
{
    vbb_DelayMillis(100); 
    
    vbb_GSM_Flush();
    if (Command != NULL && vbb_GetLen(Command) > 0)        
        vbb_Message_Init(&gsmMessage);
    vbb_USART_Print(&gsmUsart, Command);
    
    if (vbb_DebugMode())
    {        
        vbb_OUTPUT_Print("DEBUG: Put AT command: \"");
        vbb_OUTPUT_PrintDebug(Command);
        vbb_OUTPUT_Print("\"\r\n");
    }
    
    _gsm_PreviousCmdTime = vbb_RTC_GetSec();
    _gsm_CurATCmdDone = FALSE;
    
    vbb_StrCpy(_gsm_CurATCmd, Command, vbb_GetLen(Command));
}

bool GSM_CommandFinished(void)
{
    return _gsm_CurATCmdDone;
}

char GSM_WaitForAnswerUpdate(const char * Value1, const char * Value2, const char * Value3, const char * Value4, unsigned int TimeOut)
{    
    VBB_Message * buffer = &gsmMessage;
    char answer = 0;    
    
    if (_gsm_CurATCmdDone)
        return 0;
    
    vbb_GSM_Flush();
    vbb_CleanMessage2(buffer->buff, buffer->length);
    
    if (buffer->length > 0)
    {            
        if (buffer->length < MESSAGE_SIZE - 1)
        {
            if (Value1 != NULL && vbb_GetLen(Value1) > 0 && vbb_FindStr(buffer->buff, Value1) != 0)
                answer = 1;
            else if (Value2 != NULL && vbb_GetLen(Value2) > 0 && vbb_FindStr(buffer->buff, Value2) != 0)
                answer = 2;
            else if (Value3 != NULL && vbb_GetLen(Value3) > 0 && vbb_FindStr(buffer->buff, Value3) != 0)
                answer = 3;
            else if (Value4 != NULL && vbb_GetLen(Value4) > 0 && vbb_FindStr(buffer->buff, Value4) != 0)
                answer = 4;
        }
    }
    
    if (answer != 0 || (vbb_RTC_GetSec() - _gsm_PreviousCmdTime) > TimeOut)
    {
        _gsm_CurATCmdDone = TRUE;
        
        if (vbb_DebugMode())
        {        
            vbb_OUTPUT_PrintDebug(buffer->buff);  
            vbb_OUTPUT_Print("\r\n");
            vbb_OUTPUT_Print("DEBUG: Return of AT command \"");
            vbb_OUTPUT_PrintDebug(_gsm_CurATCmd);
            vbb_OUTPUT_Print("\" with value \"");        
            if (answer == 1)
                vbb_OUTPUT_PrintDebug(Value1);
            else if (answer == 2)
                vbb_OUTPUT_PrintDebug(Value2);
            else if (answer == 3)
                vbb_OUTPUT_PrintDebug(Value3);
            else if (answer == 4)
                vbb_OUTPUT_PrintDebug(Value4);
            else
                vbb_OUTPUT_PrintDebug("NULL");
            vbb_OUTPUT_Print("\".\r\n");
        }
    }
    
    return answer;
}

char GSM_Command(const char * Command, const char * Value1, const char * Value2, const char * Value3, const char * Value4, unsigned int TimeOut)
{
    char answer = 0;
    
    GSM_PutCommand(Command);
    while (!_gsm_CurATCmdDone)
    {
        answer = GSM_WaitForAnswerUpdate(Value1, Value2, Value3, Value4, TimeOut);
        vbb_DelayMillis(25);
    }
    
    return answer;
}

char GSM_WriteByte(char Byte)
{
    vbb_USART_WriteByte(&gsmUsart, Byte);
    return TRUE;
}

void GSM_Reboot(void)
{
    if (vbb_DeviceModel() == CNS01)
        GSM_Reboot_CNS01();    
}

void GSM_SetUp(void)
{
    if (vbb_DeviceModel() == CNS01)
        GSM_SetUp_CNS01();
    else if (vbb_DeviceModel() == CNS03)
        GSM_SetUp_CNS03();
    else if (vbb_DeviceModel() == BA01)
        GSM_SetUp_BA01();
}

char _gsm_url_build[256];

#define CONFIG_TIME_CHECK_BALANCE       7200
#define CONFIG_TIME_CHECK_INBOX         15
#define CONFIG_TIME_SYNC_SERVER_TIME    600
#define CONFIG_TIME_CHECK_SERVER_PUSH   360
#define MAX_RAW_SIZE 2048

int _gsm_LastCheckInboxTime;
int _gsm_LastCheckBalance;
int _gsm_LastSendData;
int _gsm_LastSyncServerTime;
unsigned int _gsm_LastSendSuccesfully;

extern VBB_GPS      gps;
extern VBB_Sensor   sensor;

void vbb_GSM_Init(void)
{        
    if (vbb_DeviceModel() == CNS03)
    {
        vbb_USART_Init(&gsmUsart, USART2, 115200, 2, gsmBuffer, GSM_BUFFER_SIZE);
        vbb_PIN_Init(&PWRKEY_Pin, GPIOB, 0, PIN_MODE_GENERAL_OUTPUT_PUSH_PULL);
        vbb_PIN_Init(&STATUS_Pin, GPIOE, 11, PIN_MODE_FLOATING_INPUT);
    }
    else if (vbb_DeviceModel() == CNS01)
    {
        vbb_USART_Init(&gsmUsart, UART5, 9600, 2, gsmBuffer, GSM_BUFFER_SIZE);
        vbb_PIN_Init(&PWRKEY_Pin, GPIOE, 13, PIN_MODE_GENERAL_OUTPUT_PUSH_PULL);
        vbb_PIN_Init(&STATUS_Pin, GPIOE, 15, PIN_MODE_GENERAL_OUTPUT_PUSH_PULL);
    }
    else if (vbb_DeviceModel() == BA01)
    {
#ifndef NEED_FIX_BUG        
        vbb_USART_Init(&gsmUsart, UART5, 115200, 2, gsmBuffer, GSM_BUFFER_SIZE);
#endif        
        vbb_PIN_Init(&PWRKEY_Pin, GPIOE, 13, PIN_MODE_GENERAL_OUTPUT_PUSH_PULL);
        vbb_PIN_Init(&STATUS_Pin, GPIOE, 15, PIN_MODE_FLOATING_INPUT);
    }
    
    /*
    GSM_Reboot();
    GSM_SetUp();    
    
    vbb_CONSOLE_Printf("INFO: GSM Module: \"%s\"\r\n", vbb_GSM_GetIdentity());
    vbb_CONSOLE_Printf("INFO: IMEI: \"%s\"\r\n", vbb_GSM_GetIMEI());    
    */
    
    _gsm_LastSyncServerTime = 0;
    _gsm_LastCheckBalance = 0;
    _gsm_LastCheckInboxTime = 0;
    _gsm_LastSyncServerTime = 0;
    communication_inprocess = FALSE;
    server_online = 0;
    
    GSM_State = GSM_BEFORE_REBOOT;
}

void vbb_GSM_Flush(void)
{
    uint8_t c;

    VBB_DAT_Queue * queue = &(gsmUsart.rBuffer);
    VBB_Message * msg = &gsmMessage;
    
    while (queue->head < queue->tail)
    {
        c = queue->buffer[queue->head & queue->bufferSizeMinusOne];       
        queue->head++; 
        if (c > 0) 
        {
            vbb_Message_AddByte(msg, c);
            /*
            if (c == '\r')
            {
                vbb_OUTPUT_WriteByte('<');
                vbb_OUTPUT_WriteByte('C');
                vbb_OUTPUT_WriteByte('R');
                vbb_OUTPUT_WriteByte('>');
            }
            else if (c == '\n')
            {
                vbb_OUTPUT_WriteByte('<');
                vbb_OUTPUT_WriteByte('L');
                vbb_OUTPUT_WriteByte('F');
                vbb_OUTPUT_WriteByte('>');
            }
            else
                vbb_OUTPUT_WriteByte(c);
            */
        }
    }
}

char * vbb_GSM_GetIdentity(void)
{
    char * _tmp = NULL;
    
    if (vbb_GetLen(_gsm_Identity) > 0)
        return _gsm_Identity;
    else
    {
        if (GSM_Command("ATE1\r", "OK", "ERROR", NULL, NULL, 5) == 1)
        {        
            if (GSM_Command("AT+CGMM\r", "OK", "ERROR", NULL, NULL, 5) == 1)
            {
                _tmp = gsmMessage.buff;
                _gsm_tmp[0] = 0;
                vbb_ExtractStr(_gsm_tmp, _tmp, "AT+CGMM\r\r\n", "\r\n\r\nOK");
                if (vbb_GetLen(_gsm_tmp) > 0)                    
                    vbb_StrCpy(_gsm_Identity, _gsm_tmp, vbb_GetLen(_gsm_tmp));
            }
            GSM_Command("ATE0\r", "OK", "ERROR", NULL, NULL, 5);
        }        
        return _gsm_Identity;
    }
}

char * vbb_GSM_GetIMEI(void)
{
    char * _tmp = NULL;
    
    if (vbb_GetLen(_gsm_IMEI) > 0)
        return _gsm_IMEI;
    else
    {
        if (GSM_Command("ATE1\r", "OK", "ERROR", NULL, NULL, 5) == 1)
        {
            if (GSM_Command("AT+GSN\r", "OK", "ERROR", NULL, NULL, 5) == 1)
            {
                _tmp = gsmMessage.buff;
                _gsm_tmp[0] = 0;
                vbb_ExtractStr(_gsm_tmp, _tmp, "AT+GSN\r\r\n", "\r\n\r\nOK");
                if (vbb_GetLen(_gsm_tmp) > 0)                    
                    vbb_StrCpy(_gsm_IMEI, _gsm_tmp, vbb_GetLen(_gsm_tmp));
            }
            GSM_Command("ATE0\r", "OK", "ERROR", NULL, NULL, 5);
        }
        return _gsm_IMEI;
    }
}

char _gsm_tmpCmd[256];
char _gsm_tmpCmd2[256];

bool vbb_GSM_SendSMS(const char * Number, const char * MessageBody)
{
    GSM_Command("AT+CMGF=1\r", "OK", "ERROR", NULL, NULL, 5);
    sprintf(_gsm_tmpCmd, "AT+CMGS=\"%s\"\r", Number);
    if (GSM_Command(_gsm_tmpCmd, ">", "ERROR", NULL, NULL, 5) == 1)
    {
        vbb_USART_Print(&gsmUsart, MessageBody);
        vbb_USART_WriteByte(&gsmUsart, 0x1A);
        if (GSM_Command("", "OK", "ERROR", NULL, NULL, 10) == 1)
            return TRUE;
    }    
    return FALSE;
}

void GSM_DeleteSMS(int SMSIndex)
{
    unsigned int try_count = 5;
    while (try_count > 0)
    {
        sprintf(_gsm_tmpCmd, "AT+CMGD=%d\r", SMSIndex);
        if (GSM_Command(_gsm_tmpCmd, "OK", "ERROR", NULL, NULL, 10) == 1)
        {
            vbb_OUTPUT_Print("INFO: Message "); 
            vbb_OUTPUT_PrintNumberSigned(SMSIndex);
            vbb_OUTPUT_Print(" deleted.\r\n");
        }
        vbb_DelayMillis(200);        
        try_count--;
    }
}

char str_cusd[256];

char * GSM_QueryCUSD(const char * RequestStr)
{
    char * buff;
    int i = 0;
    char * tmpStr;
    
    str_cusd[0] = 0;
    
    sprintf(_gsm_tmpCmd, "AT+CUSD=1,\"%s\"\r", RequestStr);
    if (GSM_Command(_gsm_tmpCmd, "+CUSD:", "\r\nERROR\r\n", NULL, NULL, 10) == 1)
    {        
        vbb_DelayMillis(200);
        vbb_GSM_Flush();
        
        buff = vbb_FindStr(gsmMessage.buff, "+CUSD:");        
        if (buff)
        {
            tmpStr = buff + vbb_GetLen("+CUSD: 0,\"");        
            vbb_StrCpy(str_cusd, tmpStr, vbb_GetLen(tmpStr));
            i = 0;
            while (i < 256 && str_cusd[i] != 0 && str_cusd[i] != '\"')
                i++;
            if (i < 256 && str_cusd[i] == '\"')
                str_cusd[i] = 0;               
            else
                str_cusd[0] = 0;
        }
    }
    return str_cusd;
}

void vbb_GSM_QueryCUSD(const char * CUSD_Query)
{
    char * retStr = GSM_QueryCUSD(CUSD_Query);
    if (retStr)
    {
        //vbb_FLASHMEM_PushMessageF("$MSG,%d,%s,%s,%s,%02d%02d%02d,%02d%02d%02d,\"%s\",\"%s\"",
        vbb_FLASHMEM_PushMessageF("$MSG,%d,%s,%s,%02d%02d%02d,%02d%02d%02d,\"%s\",\"%s\"",
                VERSION,
                storage.config.OwnerId,
                storage.config.VehicleId,
                /*storage.config.PlateNumber,*/
                rtc.hour,
                rtc.minute,
                rtc.second,
                rtc.day,                
                rtc.month,
                rtc.year % 100,
                "TELCO",
                retStr);   
    }
}

char sms_body[256];
char sms_receiv[32];

char p_num[30];
char p_sms[256];

bool GSM_CheckInbox(void)
{
    char * line_sms;
    char * num = p_num;
    char * sms = p_sms;
    int idx;
    
    GSM_Command("AT+CMGF=1\r", "OK", "ERROR", NULL, NULL, 5);
    if (GSM_Command("AT+CMGL=\"ALL\"\r", "\r\nOK\r\n", "\r\nERROR\r\n", NULL, NULL, 10) != 2)
    {   
        line_sms = vbb_FindStr(gsmMessage.buff, "+CMGL: ");            
        if (line_sms)
        {                                
            line_sms += vbb_GetLen("+CMGL: ");
            
            num = p_num;
            sms = p_sms;
            
            sscanf(line_sms, "%d", &idx);
            line_sms = vbb_FindStr(line_sms, "READ\",\"");
            line_sms += vbb_GetLen("READ\",\"");
            
            while (line_sms[0] != '"')
            {
                num[0] = line_sms[0];
                num++;
                line_sms++;
            }
            num[0] = 0;
            
            line_sms = vbb_FindStr(line_sms, "\"\r\n");
            line_sms += vbb_GetLen("\"\r\n");
            
            while (TRUE)
            {
                if (vbb_StartWith(line_sms, "+CMGL:"))
                    break;                
             
                if (vbb_StartWith(line_sms, "\r\nOK"))
                    break;
                
                sms[0] = line_sms[0];
                sms++;
                line_sms++;                                
            }
            sms[0] = 0;
            
            vbb_CleanMessage(p_sms, "\r\n\"");            
            vbb_OUTPUT_Print("INFO: Got a new SMS \"");
            vbb_OUTPUT_PrintDebug(p_num);
            vbb_OUTPUT_Print("\" \"");
            vbb_OUTPUT_PrintDebug(p_sms);
            vbb_OUTPUT_Print("\"\r\n");
            
            GSM_DeleteSMS(idx);            
            vbb_ProcessCommand(p_num, p_sms);
            //vbb_FLASHMEM_PushMessageF("$MSG,%d,%s,%s,%s,%02d%02d%02d,%02d%02d%02d,\"%s\",\"%s\"",
            vbb_FLASHMEM_PushMessageF("$MSG,%d,%s,%s,%02d%02d%02d,%02d%02d%02d,\"%s\",\"%s\"",
                VERSION,
                storage.config.OwnerId,
                storage.config.VehicleId,
                /*storage.config.PlateNumber,*/
                rtc.hour,
                rtc.minute,
                rtc.second,
                rtc.day,                
                rtc.month,
                rtc.year % 100,
                p_num,
                p_sms); 
        }
        vbb_OUTPUT_Print("DEBUG: Check SMS inbox successfully.\r\n");
        return TRUE;
    }
    else
    {
        vbb_OUTPUT_Print("DEBUG: Check SMS inbox failed.\r\n");
        return FALSE;    
    }             
}

void GSM_LedSignal_NoGSM(void)
{
    vbb_LedSwitch(TRUE, TRUE); 
    vbb_DelayMillis(500);
    vbb_LedSwitch(TRUE, FALSE);
}

int GSM_GetSignalQuality(void)
{
    char * csq_chars;
    if (GSM_Command("AT+CSQ\r\n", "+CSQ: ", "ERROR", NULL, NULL, 10) == 1)
    {
        vbb_DelayMillis(200);
        vbb_GSM_Flush();
            
        csq_chars = vbb_FindStr(gsmMessage.buff, "+CSQ:");
        sscanf(csq_chars + vbb_GetLen("+CSQ: "), "%d", &GSM_SignalQuality);                    
    }
    else
        GSM_SignalQuality = 0;
    
    return GSM_SignalQuality;
}

#define MAX_RAW_BUFFER 8192
char BufferA[MAX_RAW_BUFFER];
char BufferB[MAX_RAW_BUFFER];

void vbb_GSM_StartDownloadFirmware(unsigned int Version, bool OverrideCurDownload)
{
    unsigned int downloadAddr;
    if (!vbb_BACKUP_Get(SAVE_GSM_FIRMWARE_DOWNLOADING) || OverrideCurDownload)
    {
        vbb_BACKUP_Set(SAVE_GSM_FIRMWARE_DOWNLOADING, TRUE);
        vbb_BACKUP_Set(SAVE_GSM_FIRMWARE_TO_PROGRAM, FALSE);
        vbb_BACKUP_SetInt(SAVE_GSM_DOWNLOAD_VERSION, Version);
        downloadAddr = FLASH_HEX_ADDR;
        vbb_BACKUP_Set(SAVE_GSM_DOWNLOAD_ADDR, (downloadAddr & 0xFFFF0000) >> 16);
        vbb_BACKUP_Set(SAVE_GSM_DOWNLOAD_ADDR + 1, (downloadAddr & 0x0000FFFF));        
        vbb_BACKUP_Set(SAVE_GSM_DOWNLOAD_INDEX, 0);
        
        downloadAddr = FLASH_HEX_ADDR;
        while (downloadAddr < FLASH_END)
        {
            vbb_CONSOLE_Printf("INFO: Erase flash page 0x%X\r\n", downloadAddr);
            vbb_INFLASH_ErasePage(downloadAddr);
            downloadAddr += FLASH_PAGE_SIZE;
        }
        
        vbb_CONSOLE_Printf("INFO: Start download firmware v%d.%02d.\r\n", Version / 100, Version % 100);
        
        vbb_Reset();
    }
}

void vbb_GSM_CancelDownloadFirmware(void)
{
    vbb_BACKUP_Set(SAVE_GSM_FIRMWARE_DOWNLOADING, FALSE);
    vbb_BACKUP_Set(SAVE_GSM_FIRMWARE_TO_PROGRAM, FALSE);
    vbb_OUTPUT_Print("INFO: Stop downloading firmware if any.\r\n");
}

bool vbb_GSM_MakeMuteCall(const char * Number)
{
    char * cmd = vbb_BuildString("ATD%s;\r", Number);
    GSM_Command(cmd, "OK", "ERROR", NULL, NULL, 10);
    return TRUE;
}

char _gsm_server_cmd[256];
int _gsm_server_cmdLen;
int _gsm_LastCheckServerPush;

enum {
    COMM_NONE = 0,
    COMM_SYNC_TIME,
    COMM_POST_DATA,
    COMM_CHECK_SERVER_COMMAND,
    COMM_DOWNLOAD,
};

int gsm_LastCommunication;
int gsm_SendCount;

extern unsigned bool cab_occupied;

bool GSM_ServerLog(void)
{
    int _msgPerHour;    
    int _dataLen;
    int _msgLen;    
    char * _url;
    char ret;
    
    if (GSM_Communication_InProcess())
        return FALSE;
    
    if (vbb_GPS_OnMoving() || sensor.EngineStarted || cab_occupied)
        _msgPerHour = storage.config.MessagePerHour_Move;
    else
        _msgPerHour = storage.config.MessagePerHour_Idle;
    
    if (vbb_RTC_GetSec() - _gsm_LastSendData > 3600 / _msgPerHour)
    {
        gsm_SendCount = 0;
        _dataLen = 0;
        
        if (flashMem.head > flashMem.sendhead)
            flashMem.sendhead = flashMem.head;
        
        if (flashMem.tail > flashMem.sendhead)
        {
            vbb_OUTPUT_Print("INFO: Start sending messages.\r\n");
            
            while (flashMem.tail > flashMem.sendhead + gsm_SendCount)
            {
                vbb_FLASHMEM_GetMessage(flashMem.sendhead + gsm_SendCount);                
                if (!vbb_FindStr(flashMem.tmpmsg, "$GPS,"))
                {
                    vbb_OUTPUT_PrintDebug(flashMem.tmpmsg);
                    vbb_OUTPUT_Print("\r\n");             
                    
                    _msgLen = vbb_GetLen(flashMem.tmpmsg);
                    if (_dataLen + _msgLen < MAX_RAW_SIZE)
                    {
                        vbb_StrCpy(BufferA + _dataLen, flashMem.tmpmsg, _msgLen);
                        _dataLen += _msgLen;
                        gsm_SendCount++;
                    }
                    else
                        break;
                }
                else
                {
                    gsm_SendCount++;
                }
            }
            BufferA[_dataLen] = 0;
            
            if (_dataLen >= 256)
            {
                _url = vbb_BuildString("http://%s:%s/p3.php?v=%s&o=%s&f=bin", storage.config.HTTPServerAddr, storage.config.HTTPServerPort, storage.config.VehicleId, storage.config.OwnerId);                
                ret = GSM_Communication_Start(_url, BufferA, _dataLen, BufferB, TRUE, (vbb_GPS_OnMoving() || sensor.EngineStarted || cab_occupied || vbb_BACKUP_Get(SAVE_GSM_FIRMWARE_DOWNLOADING)), 15);
                if (ret)
                {
                    gsm_LastCommunication = COMM_POST_DATA;
                    return ret;
                }
            }
        }
    }
    
    return FALSE;
}

bool GSM_ServerLog_Success(char * ResponseBuffer, int ResponseSize)
{
    char * cmds;
    
    if (gsm_LastCommunication != COMM_POST_DATA)
        return FALSE;
    
    vbb_OUTPUT_Print("INFO: Server response: ");
    ResponseBuffer[ResponseSize] = 0;
    vbb_OUTPUT_PrintDebug(ResponseBuffer);
    vbb_OUTPUT_Print("\r\n");
                        
    // Process server push messages
    cmds = vbb_FindStr(ResponseBuffer, "OK\r\n");
    if (cmds)
    {
        cmds += vbb_GetLen("OK\r\n");
                            
        if (cmds[0] != 0)
        {
            _gsm_server_cmdLen = 0;
                                
            while (cmds[0] != 0)
            {
                if (cmds[0] == '|')
                {
                    if (_gsm_server_cmdLen > 0)
                    {
                        _gsm_server_cmd[_gsm_server_cmdLen] = 0;                    
                        vbb_ProcessCommand(NULL, _gsm_server_cmd);                                        
                        _gsm_server_cmdLen = 0;
                    }
                }
                else
                {
                    _gsm_server_cmd[_gsm_server_cmdLen] = cmds[0];
                    _gsm_server_cmdLen++;                
                }
                cmds++;
            }
        }                   
    }
                        
    flashMem.sendhead += gsm_SendCount;    
    vbb_SAVE_SaveFlashInfo(&flashMem);
    
    _gsm_LastSendData = vbb_RTC_GetSec();
    _gsm_LastSendSuccesfully = vbb_RTC_GetCurrSec();    
    return TRUE;
}

bool GSM_DownloadFirmware(void)
{
    char * url;         
    int ret;
    
    if (GSM_Communication_InProcess())
        return FALSE;        
    
    if (vbb_BACKUP_Get(SAVE_GSM_FIRMWARE_DOWNLOADING))
    {                
        url = vbb_BuildString("http://%s:%s/r.php?f=\\%s\\%s_%03d.hex&s=%d&c=10", storage.config.HTTPServerAddr, storage.config.HTTPServerPort, storage.config.FirmwareDir, storage.config.FirmwarePrefix, vbb_BACKUP_GetInt(SAVE_GSM_DOWNLOAD_VERSION), vbb_BACKUP_Get(SAVE_GSM_DOWNLOAD_INDEX) * 10);        
        
        ret = GSM_Communication_Start(url, BufferA, 0, BufferB, FALSE, (vbb_GPS_OnMoving() || sensor.EngineStarted || cab_occupied || vbb_BACKUP_Get(SAVE_GSM_FIRMWARE_DOWNLOADING)), 15);
        if (ret)
        {
            gsm_LastCommunication = COMM_DOWNLOAD;
            return ret;
        } 
    }
    return FALSE;
}

bool GSM_DownloadFirmware_Success(char * ResponseBuffer, int ResponseSize)
{
    unsigned int retAddr;
    unsigned int downloadAddr;   
    
    if (ResponseSize > 0)
    {
        downloadAddr = vbb_BACKUP_GetInt(SAVE_GSM_DOWNLOAD_ADDR);    
        
        retAddr = vbb_INFLASH_Write(downloadAddr, ResponseBuffer, ResponseSize);
        if (retAddr == 0)
        {
            vbb_OUTPUT_Print("INFO: An error occurs when write firmware into Flash. Cancel download new firmware.\r\n");
            vbb_GSM_CancelDownloadFirmware();
            
            //vbb_FLASHMEM_PushMessageF("$ERROR,%d,%s,%s,%s,%02d%02d%02d,%02d%02d%02d,\"%s\"",
            vbb_FLASHMEM_PushMessageF("$ERROR,%d,%s,%s,%02d%02d%02d,%02d%02d%02d,\"%s\"",
                VERSION,
                storage.config.OwnerId,
                storage.config.VehicleId,
                /*storage.config.PlateNumber,*/
                rtc.hour,
                rtc.minute,
                rtc.second,
                rtc.day,
                rtc.month,
                rtc.year % 100,
                "Cancel download firmware due to error in writing into flash.");
            return FALSE;
        }                
        
        downloadAddr = retAddr;
        vbb_BACKUP_Set(SAVE_GSM_DOWNLOAD_ADDR, (downloadAddr & 0xFFFF0000) >> 16);
        vbb_BACKUP_Set(SAVE_GSM_DOWNLOAD_ADDR + 1, (downloadAddr & 0x0000FFFF));
        vbb_BACKUP_Set(SAVE_GSM_DOWNLOAD_INDEX, vbb_BACKUP_Get(SAVE_GSM_DOWNLOAD_INDEX) + 1);
        return TRUE;
    }
    else
    {
        vbb_BACKUP_Set(SAVE_GSM_FIRMWARE_DOWNLOADING, FALSE);
        vbb_BACKUP_Set(SAVE_GSM_FIRMWARE_TO_PROGRAM, TRUE);
        vbb_SAVE_SaveFlashInfo(&flashMem);
        vbb_OUTPUT_Print("INFO: Reset to update firmware.\r\n");
        
        //vbb_FLASHMEM_PushMessageF("$INFO,%d,%s,%s,%s,%02d%02d%02d,%02d%02d%02d,\"Download firmware %s_%03d.hex successfully.\"",
        vbb_FLASHMEM_PushMessageF("$INFO,%d,%s,%s,%02d%02d%02d,%02d%02d%02d,\"Download firmware %s_%03d.hex successfully.\"",
                VERSION,
                storage.config.OwnerId,
                storage.config.VehicleId,
                /*storage.config.PlateNumber,*/
                rtc.hour,
                rtc.minute,
                rtc.second,
                rtc.day,
                rtc.month,
                rtc.year % 100,
                storage.config.FirmwarePrefix,
                vbb_BACKUP_GetInt(SAVE_GSM_DOWNLOAD_VERSION));
        
        vbb_Reset();
        return TRUE;
    }
}

bool GSM_CheckServerCommand(void)
{
    char * url; 
    char ret;
    
    if (GSM_Communication_InProcess())
        return FALSE;
    
    vbb_OUTPUT_Print("INFO: Check server push messages.\r\n");
    url = vbb_BuildString("http://%s:%s/s.php?v=%s", storage.config.HTTPServerAddr, storage.config.HTTPServerPort, storage.config.VehicleId);    
    
    ret = GSM_Communication_Start(url, BufferA, 0, BufferB, FALSE, (vbb_GPS_OnMoving() || sensor.EngineStarted || cab_occupied || vbb_BACKUP_Get(SAVE_GSM_FIRMWARE_DOWNLOADING)), 15);
    if (ret)
        gsm_LastCommunication = COMM_CHECK_SERVER_COMMAND;
    
    return ret;
}

bool GSM_CheckServerCommand_Success(char * ResponseBuffer, int ResponseSize)
{
    char * cmds;
    
    // Process server push messages
    cmds = vbb_FindStr(ResponseBuffer, "OK\r\n");
    if (cmds)
    {
        cmds += vbb_GetLen("OK\r\n");
                            
        if (cmds[0] != 0)
        {
            _gsm_server_cmdLen = 0;
                                
            while (cmds[0] != 0)
            {
                if (cmds[0] == '|')
                {
                    if (_gsm_server_cmdLen > 0)
                    {
                        _gsm_server_cmd[_gsm_server_cmdLen] = 0;                    
                        vbb_ProcessCommand(NULL, _gsm_server_cmd);                                        
                        _gsm_server_cmdLen = 0;
                    }
                }
                else
                {
                    _gsm_server_cmd[_gsm_server_cmdLen] = cmds[0];
                    _gsm_server_cmdLen++;                
                }
                cmds++;
            }
        }                   
    }
    
    return FALSE;
}

bool GSM_SyncServerTime(void)
{
    char * url; 
    char ret = FALSE;        
    
    if (vbb_RTC_GetSec() - _gsm_LastSyncServerTime > CONFIG_TIME_SYNC_SERVER_TIME)
    {
        url = vbb_BuildString("http://%s:%s/t.php", storage.config.HTTPServerAddr, storage.config.HTTPServerPort);
        vbb_StrCpy(_gsm_url_build, url, vbb_GetLen(url));
        
        ret = GSM_Communication_Start(url, BufferA, 0, BufferB, FALSE, (vbb_GPS_OnMoving() || sensor.EngineStarted || cab_occupied || vbb_BACKUP_Get(SAVE_GSM_FIRMWARE_DOWNLOADING)), 15);
        if (ret)
            gsm_LastCommunication = COMM_SYNC_TIME;
    }
    
    return ret;    
}

bool GSM_SyncServerTime_Success(char * ResponseBuffer, int ResponseSize)
{
    unsigned int curTime;
    
    sscanf(ResponseBuffer, "%u", &curTime);  
    vbb_RTC_Set(curTime);
    vbb_RTC_Update(&rtc);
    vbb_CONSOLE_Printf("INFO: Current time is %02d:%02d:%02d %02d/%02d/%04d.\r\n", rtc.hour, rtc.minute, rtc.second, rtc.day, rtc.month, rtc.year);
    
    _gsm_LastSyncServerTime = vbb_RTC_GetSec();    
    gsm_LastCommunication = COMM_NONE;
    return TRUE;
}

void GSM_ResponseProcess(char * ResponseBuffer, int ResponseSize)
{
    switch (gsm_LastCommunication)
    {
        case COMM_SYNC_TIME:
        {     
            vbb_OUTPUT_Print("INFO: Got server time syncronization message.\r\n");
            GSM_SyncServerTime_Success(ResponseBuffer, ResponseSize);
            break;
        }
        
        case COMM_POST_DATA:
        {
            vbb_OUTPUT_Print("INFO: Got server response from data post.\r\n");
            _gsm_LastSendSuccesfully = vbb_RTC_GetCurrSec();
            GSM_ServerLog_Success(ResponseBuffer, ResponseSize);
            break;
        }
        
        case COMM_DOWNLOAD:
        {
            vbb_OUTPUT_Print("INFO: Got firmware data from downloading.\r\n");
            GSM_DownloadFirmware_Success(ResponseBuffer, ResponseSize);
            break;
        }
        
        case COMM_CHECK_SERVER_COMMAND:
        {
            vbb_OUTPUT_Print("INFO: Got server command checking response.\r\n");
            GSM_CheckServerCommand_Success(ResponseBuffer, ResponseSize);
            break;
        }
    }
}

void GSM_Communication_ProcessResponse(char * ResponseBuffer, int ResponseSize)
{
    GSM_ResponseProcess(ResponseBuffer, ResponseSize);
}

void GSM_AnswerCall(void)
{    
    vbb_GSM_Flush();
    if (vbb_FindStr(gsmMessage.buff, "RING"))
    {
        GSM_Command("ATA\r", "OK", "ERROR", NULL, NULL, 5);    
        vbb_Message_Init(&gsmMessage);
    }
}

extern void MAIN_Booted(void);

#define MAX_GSM_LIVE_TIME 7200
unsigned int timeOutGSM_ModuleLive;
unsigned int GSM_Live_TimeOn;

void vbb_GSM_Update(void)
{                    
    switch (GSM_State)
    {                
        case GSM_READY:
        {                            
            if (GSM_Communication_InProcess())
                GSM_Communication_Update(); 
            else
            {
                if (vbb_RTC_GetSec() - GSM_Live_TimeOn >= MAX_GSM_LIVE_TIME) 
                {
                    vbb_OUTPUT_Print("INFO: Reboot GSM module because of living for too long.\r\n");
                    GSM_State = GSM_BEFORE_REBOOT;
                }
                
                GSM_AnswerCall();            
                
                //Regularly check sms inbox for every minutes    
                if (vbb_RTC_GetSec() - _gsm_LastCheckInboxTime > CONFIG_TIME_CHECK_INBOX)
                {
                    _gsm_LastCheckInboxTime = vbb_RTC_GetSec();                
                    GSM_CheckInbox();
                    GSM_GetSignalQuality();
                }
                
                if (vbb_RTC_GetSec() - _gsm_LastCheckBalance > CONFIG_TIME_CHECK_BALANCE)
                {
                    _gsm_LastCheckBalance = vbb_RTC_GetSec();
                    vbb_GSM_QueryCUSD("*101#");        
                }
                
                if (vbb_RTC_GetSec() - _gsm_LastCheckServerPush > CONFIG_TIME_CHECK_SERVER_PUSH)
                {
                    _gsm_LastCheckServerPush = vbb_RTC_GetSec();
                    GSM_CheckServerCommand();
                }
                
                GSM_SyncServerTime();
                GSM_ServerLog();    
                GSM_DownloadFirmware();
            }
            break;
        }
        
        case GSM_BEFORE_REBOOT:
        {                                        
            if (vbb_DeviceModel() == CNS01)
            {
                GSM_Reboot();
                GSM_State = GSM_TURNED_ON;
            }
            else
            {                
                if (GSM_IsOn())
                {
                    if (vbb_DeviceModel() == CNS03 || vbb_DeviceModel() == BA01)
                    {
                        vbb_OUTPUT_Print("DEBUG: Switch off GSM module.\r\n");
                        GSM_SwitchOff();
                        GSM_State = GSM_TURNED_OFF;
                    }
                }                
                else
                {
                    vbb_OUTPUT_Print("DEBUG: GSM module is off already.\r\n");
                    GSM_State = GSM_TURNED_OFF;
                }
            }
            break;
        }
        
        case GSM_TURNED_OFF:
        {            
            if (GSM_IsOff())
            {
                vbb_OUTPUT_Print("DEBUG: Switch on GSM module.\r\n");
                GSM_SwitchOn();
                GSM_State = GSM_TURNED_ON;
            }            
            break;
        }
        
        case GSM_TURNED_ON:
        {
            if (GSM_IsOn())
            {                
                GSM_State = GSM_SETUP;                
            }            
            break;
        }
        
        case GSM_SETUP:
        {            
            if (GSM_Ready())
            {                                
                GSM_SetUp();                                
                
                vbb_CONSOLE_Printf("INFO: GSM Module: \"%s\"\r\n", vbb_GSM_GetIdentity());
                vbb_CONSOLE_Printf("INFO: IMEI: \"%s\"\r\n", vbb_GSM_GetIMEI());    
                
                MAIN_Booted();
                
                GSM_Live_TimeOn = vbb_RTC_GetSec();
                GSM_State = GSM_READY;
            }
            break;
        }
    }        
}

#endif
